{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 语言模型\n",
    "\n",
    "语言模型（language model）是自然语言处理的重要技术。自然语言处理中最常见的数据是文本数据。我们可以把一段自然语言文本看作一段离散的时间序列。假设一段长度为$T$的文本中的词依次为$w_1, w_2, \\ldots, w_T$，那么在离散的时间序列中，$w_t$（$1 \\leq t \\leq T$）可看作在时间步（time step）$t$的输出或标签。给定一个长度为$T$的词的序列$w_1, w_2, \\ldots, w_T$，语言模型将计算该序列的概率：\n",
    "\n",
    "$$P(w_1, w_2, \\ldots, w_T).$$\n",
    "\n",
    "**每一个词出现的概率，与前面的词已经出现过，再出现这个词的概率。**\n",
    "\n",
    "##  语言模型的计算\n",
    "\n",
    "既然语言模型很有用，那该如何计算它呢？假设序列$w_1, w_2, \\ldots, w_T$中的每个词是依次生成的，我们有\n",
    "\n",
    "$$P(w_1, w_2, \\ldots, w_T) = \\prod_{t=1}^T P(w_t \\mid w_1, \\ldots, w_{t-1}).$$\n",
    "\n",
    "例如，一段含有4个词的文本序列的概率\n",
    "\n",
    "$$P(w_1, w_2, w_3, w_4) =  P(w_1) P(w_2 \\mid w_1) P(w_3 \\mid w_1, w_2) P(w_4 \\mid w_1, w_2, w_3).$$\n",
    "\n",
    "我们需要计算词的概率，以及一个词在给定前几个词的情况下的**条件概率，即语言模型参数**。\n",
    "\n",
    "**例如，$P(w_1)$可以计算为$w_1$在训练数据集中的词频** （词出现的次数）**与训练数据集的总词数之比。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  $n$元语法\n",
    "\n",
    "当序列长度增加时，计算和存储多个词共同出现的概率的复杂度会呈指数级增加。$n$元语法通过马尔可夫假设（虽然并不一定成立）简化了语言模型的计算。这里的马尔可夫假设是指一个词的出现只与前面$n$个词相关，即$n$阶马尔可夫链（Markov chain of order $n$）。如果$n=1$，那么有$P(w_3 \\mid w_1, w_2) = P(w_3 \\mid w_2)$。如果基于$n-1$阶马尔可夫链，我们可以将语言模型改写为\n",
    "\n",
    "$$P(w_1, w_2, \\ldots, w_T) \\approx \\prod_{t=1}^T P(w_t \\mid w_{t-(n-1)}, \\ldots, w_{t-1}) .$$\n",
    "\n",
    "以上也叫$n$元语法（$n$-grams）。它是基于$n - 1$阶马尔可夫链的概率语言模型。当$n$分别为1、2和3时，我们将其分别称作一元语法（unigram）、二元语法（bigram）和三元语法（trigram）。例如，长度为4的序列$w_1, w_2, w_3, w_4$在一元语法、二元语法和三元语法中的概率分别为\n",
    "$$\n",
    "\\begin{aligned}\n",
    "P(w_1, w_2, w_3, w_4) &=  P(w_1) P(w_2) P(w_3) P(w_4) ,\\\\\n",
    "P(w_1, w_2, w_3, w_4) &=  P(w_1) P(w_2 \\mid w_1) P(w_3 \\mid w_2) P(w_4 \\mid w_3) ,\\\\\n",
    "P(w_1, w_2, w_3, w_4) &=  P(w_1) P(w_2 \\mid w_1) P(w_3 \\mid w_1, w_2) P(w_4 \\mid w_2, w_3) .\n",
    "\\end{aligned}\n",
    "$$\n",
    "当$n$较小时，$n$元语法往往并不准确。例如，在一元语法中，由三个词组成的句子“你走先”和“你先走”的概率是一样的。然而，当$n$较大时，$n$元语法需要计算并存储大量的词频和多词相邻频率。\n",
    "\n",
    "一元语法： 不准确，错误率高\n",
    "\n",
    "多元语法： 计算，存储量大，计算各种情况的条件概率，指数爆炸。\n",
    "\n",
    "那么，有没有方法在语言模型中更好地平衡以上这两点呢？我们将在本章探究这样的方法。\n",
    "\n",
    "## 小结\n",
    "\n",
    "- 语言模型是自然语言处理的重要技术。\n",
    "- $N$元语法是基于$n-1$阶马尔可夫链的概率语言模型，其中$n$权衡了计算复杂度和模型准确性。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 循环神经网络 RNN Recurrent Neural Network\n",
    "\n",
    "循环神经网络。它并非刚性地记忆所有固定长度的序列，例如记住所有状态下的条件概率。\n",
    "\n",
    "而是通过**隐藏状态**来存储之前时间步的信息。首先我们回忆一下前面介绍过的多层感知机，然后描述如何添加隐藏状态来将它变成循环神经网络。\n",
    "\n",
    "## 含隐藏状态的循环神经网络\n",
    "\n",
    "现在我们考虑输入数据存在时间相关性的情况。\n",
    "\n",
    "\n",
    "\n",
    "假设$\\boldsymbol{X}_t \\in \\mathbb{R}^{n \\times d}$是序列中时间步$t$的小批量输入，$\\boldsymbol{H}_t  \\in \\mathbb{R}^{n \\times h}$是该时间步的隐藏变量。\n",
    "\n",
    "> 比如输入了 $t$个小batch, 某一时间输入的是$\\boldsymbol{X}_t$ ，此时对应的隐含层输出是$\\boldsymbol{H}_t $ ,那如果不是第一个输入，应该还有前一个时刻的隐含层输出$\\boldsymbol{H}_{t-1} $ 。\n",
    "\n",
    "与多层感知机不同的是，这里我们保存上一时间步的隐藏变量$\\boldsymbol{H}_{t-1}$，并引入一个新的权重参数$\\boldsymbol{W}_{hh} \\in \\mathbb{R}^{h \\times h}$，该参数用来描述在当前时间步如何使用上一时间步的隐藏变量。具体来说，时间步$t$的隐藏变量的计算由当前时间步的输入和上一时间步的隐藏变量共同决定：\n",
    "\n",
    "$$\\boldsymbol{H}_t = \\phi(\\boldsymbol{X}_t \\boldsymbol{W}_{xh} + \\boldsymbol{H}_{t-1} \\boldsymbol{W}_{hh}  + \\boldsymbol{b}_h).$$\n",
    "\n",
    "与多层感知机相比，我们在这里添加了$\\boldsymbol{H}_{t-1} \\boldsymbol{W}_{hh}$一项。由上式中相邻时间步的隐藏变量$\\boldsymbol{H}_t$和$\\boldsymbol{H}_{t-1}$之间的关系可知，这里的隐藏变量能够捕捉截至当前时间步的序列的历史信息，就像是神经网络当前时间步的状态或记忆一样。因此，该隐藏变量也称为隐藏状态。由于隐藏状态在当前时间步的定义**使用了上一时间步的隐藏状态**，**上式的计算是循环的**。使用循环计算的网络即循环神经网络（recurrent neural network）。\n",
    "\n",
    "循环神经网络有很多种不同的构造方法。含上式所定义的隐藏状态的循环神经网络是极为常见的一种。若无特别说明，本章中的循环神经网络均基于上式中隐藏状态的循环计算。在时间步$t$，输出层的输出和多层感知机中的计算类似：\n",
    "\n",
    "$$\\boldsymbol{O}_t = \\boldsymbol{H}_t \\boldsymbol{W}_{hq} + \\boldsymbol{b}_q.$$\n",
    "\n",
    "循环神经网络的参数包括隐藏层的权重$\\boldsymbol{W}_{xh} \\in \\mathbb{R}^{d \\times h}$、$\\boldsymbol{W}_{hh} \\in \\mathbb{R}^{h \\times h}$和偏差 $\\boldsymbol{b}_h \\in \\mathbb{R}^{1 \\times h}$，以及输出层的权重$\\boldsymbol{W}_{hq} \\in \\mathbb{R}^{h \\times q}$和偏差$\\boldsymbol{b}_q \\in \\mathbb{R}^{1 \\times q}$。值得一提的是，即便在不同时间步，循环神经网络也始终使用这些模型参数。因此，循环神经网络模型参数的数量不随时间步的增加而增长。\n",
    "\n",
    "图6.1展示了循环神经网络在3个相邻时间步的计算逻辑。在时间步$t$，隐藏状态的计算可以看成是将输入$\\boldsymbol{X}_t$和前一时间步隐藏状态$\\boldsymbol{H}_{t-1}$连结后输入一个激活函数为$\\phi$的全连接层。该全连接层的输出就是当前时间步的隐藏状态$\\boldsymbol{H}_t$，且模型参数为$\\boldsymbol{W}_{xh}$与$\\boldsymbol{W}_{hh}$的连结，偏差为$\\boldsymbol{b}_h$。当前时间步$t$的隐藏状态$\\boldsymbol{H}_t$将参与下一个时间步$t+1$的隐藏状态$\\boldsymbol{H}_{t+1}$的计算，并输入到当前时间步的全连接输出层。\n",
    "\n",
    "<div align=center>\n",
    "<img width=\"500\" src=\"../imgs/rnn.png\"/>\n",
    "</div>\n",
    "<div align=center>图6.1 含隐藏状态的循环神经网络</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "\n",
    "我们刚刚提到，隐藏状态中$\\boldsymbol{X}_t \\boldsymbol{W}_{xh} + \\boldsymbol{H}_{t-1} \\boldsymbol{W}_{hh}$的计算等价于$\\boldsymbol{X}_t$与$\\boldsymbol{H}_{t-1}$连结后的矩阵乘以$\\boldsymbol{W}_{xh}$与$\\boldsymbol{W}_{hh}$连结后的矩阵。接下来，我们用一个具体的例子来验证这一点。首先，我们构造矩阵`X`、`W_xh`、`H`和`W_hh`，它们的形状分别为(3, 1)、(1, 4)、(3, 4)和(4, 4)。将`X`与`W_xh`、`H`与`W_hh`分别相乘，再把两个乘法运算的结果相加，得到形状为(3, 4)的矩阵。\n",
    "\n",
    "```python\n",
    "import torch\n",
    "\n",
    "X, W_xh = torch.randn(3, 1), torch.randn(1, 4)\n",
    "H, W_hh = torch.randn(3, 4), torch.randn(4, 4)\n",
    "torch.matmul(X, W_xh) + torch.matmul(H, W_hh)\n",
    "```\n",
    "\n",
    "输出：\n",
    "\n",
    "```\n",
    "tensor([[ 5.2633, -3.2288,  0.6037, -1.3321],\n",
    "        [ 9.4012, -6.7830,  1.0630, -0.1809],\n",
    "        [ 7.0355, -2.2361,  0.7469, -3.4667]])\n",
    "```\n",
    "\n",
    "将矩阵`X`和`H`按列（维度1）连结，连结后的矩阵形状为(3, 5)。可见，连结后矩阵在维度1的长度为矩阵`X`和`H`在维度1的长度之和（$1+4$）。然后，将矩阵`W_xh`和`W_hh`按行（维度0）连结，连结后的矩阵形状为(5, 4)。最后将两个连结后的矩阵相乘，得到与上面代码输出相同的形状为(3, 4)的矩阵。\n",
    "\n",
    "```python\n",
    "torch.matmul(torch.cat((X, H), dim=1), torch.cat((W_xh, W_hh), dim=0))\n",
    "```\n",
    "\n",
    "输出：\n",
    "\n",
    "```\n",
    "tensor([[ 5.2633, -3.2288,  0.6037, -1.3321],\n",
    "        [ 9.4012, -6.7830,  1.0630, -0.1809],\n",
    "        [ 7.0355, -2.2361,  0.7469, -3.4667]])\n",
    "```\n",
    "\n",
    "这里说的是一个RNN计算实现上的一个tricks， \n",
    "\n",
    "`torch.matmul(X, W_xh) + torch.matmul(H, W_hh) =  torch.matmul(torch.cat((X, H), dim=1), torch.cat((W_xh, W_hh), dim=0))`\n",
    "\n",
    "## 应用：基于字符级循环神经网络的语言模型\n",
    "\n",
    "最后我们介绍如何应用循环神经网络来构建一个语言模型。设小批量中样本数为1，文本序列为“想”“要”“有”“直”“升”“机”。图6.2演示了如何使用循环神经网络基于当前和过去的字符来预测下一个字符。在训练时，我们对每个时间步的输出层输出使用softmax运算，然后使用交叉熵损失函数来计算它与标签的误差。在图6.2中，由于隐藏层中隐藏状态的循环计算，时间步3的输出$\\boldsymbol{O}_3$取决于文本序列“想”“要”“有”。 由于训练数据中该序列的下一个词为“直”，时间步3的损失将取决于该时间步基于序列“想”“要”“有”生成下一个词的概率分布与该时间步的标签“直”。\n",
    "\n",
    "<div align=center>\n",
    "<img width=\"500\" src=\"../imgs/rnn_train.png\"/>\n",
    "</div>\n",
    "<div align=center>图6.2 基于字符级循环神经网络的语言模型。</div>\n",
    "\n",
    "因为每个输入词是一个字符，因此这个模型被称为字符级循环神经网络（character-level recurrent neural network）。因为不同字符的个数远小于不同词的个数（对于英文尤其如此），所以字符级循环神经网络的计算通常更加简单。在接下来的几节里，我们将介绍它的具体实现。\n",
    "\n",
    "## 小结\n",
    "\n",
    "- 使用循环计算的网络即循环神经网络。\n",
    "- 循环神经网络的隐藏状态可以捕捉截至当前时间步的序列的历史信息。\n",
    "- 循环神经网络模型参数的数量不随时间步的增加而增长。\n",
    "- 可以基于字符级循环神经网络来创建语言模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 语言模型数据集（周杰伦专辑歌词）\n",
    "\n",
    "本节将介绍如何预处理一个语言模型数据集，并将其转换成字符级循环神经网络所需要的输入格式。为此，我们收集了周杰伦从第一张专辑《Jay》到第十张专辑《跨时代》中的歌词，并在后面几节里应用循环神经网络来训练一个语言模型。当模型训练好后，我们就可以用这个模型来创作歌词。\n",
    "\n",
    "## 6.3.1 读取数据集\n",
    "\n",
    "首先读取这个数据集，看看前40个字符是什么样的。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'想要有直升机\\n想要和你飞到宇宙去\\n想要和你融化在一起\\n融化在宇宙里\\n我每天每天每'"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import random\n",
    "import zipfile\n",
    "import sys\n",
    "sys.path.append(\"..\") \n",
    "\n",
    "with zipfile.ZipFile('../Data/jaychou_lyrics.txt.zip') as zin:\n",
    "    with zin.open('jaychou_lyrics.txt') as f:\n",
    "        corpus_chars = f.read().decode('utf-8')\n",
    "corpus_chars[:40]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个数据集有6万多个字符。为了打印方便，我们把换行符替换成空格，然后仅使用前1万个字符来训练模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10000"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(corpus_chars)\n",
    "corpus_chars = corpus_chars.replace('\\n',' ').replace('\\r', ' ')\n",
    "corpus_chars = corpus_chars[:10000]\n",
    "len(corpus_chars)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 建立字库 字符索引\n",
    "### 去重，把歌词里不重复的字取出来，建立词库，然后建立歌词对应于词库的索引表"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1027\n"
     ]
    }
   ],
   "source": [
    "vob_set = set(corpus_chars)\n",
    "char_to_idx = dict([(char, i ) for i, char in enumerate(list(vob_set)) ])\n",
    "print(len(char_to_idx))\n",
    "idx_to_char = list(vob_set)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "chars: 想要有直升机 想要和你飞到宇宙去 想要和\n",
      "indices: [651, 891, 377, 997, 355, 1019, 128, 651, 891, 871, 1016, 533, 201, 751, 220, 171, 128, 651, 891, 871]\n"
     ]
    }
   ],
   "source": [
    "corpus_indices = [char_to_idx[char] for char in corpus_chars]\n",
    "sample = corpus_indices[:20]\n",
    "print('chars:', ''.join([idx_to_char[idx] for idx in sample]))\n",
    "print('indices:', sample)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "651"
      ]
     },
     "execution_count": 58,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "char_to_idx['想']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'乌'"
      ]
     },
     "execution_count": 59,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "idx_to_char[598]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data_jay_lyrics():\n",
    "    \"\"\"加载周杰伦歌词数据集\"\"\"\n",
    "    with zipfile.ZipFile('../Data/jaychou_lyrics.txt.zip') as zin:\n",
    "        with zin.open('jaychou_lyrics.txt') as f:\n",
    "            corpus_chars = f.read().decode('utf-8')\n",
    "    corpus_chars = corpus_chars.replace('\\n', ' ').replace('\\r', ' ')\n",
    "    corpus_chars = corpus_chars[0:10000]\n",
    "    idx_to_char = list(set(corpus_chars))\n",
    "    char_to_idx = dict([(char, i) for i, char in enumerate(idx_to_char)])\n",
    "    vocab_size = len(char_to_idx)\n",
    "    corpus_indices = [char_to_idx[char] for char in corpus_chars]\n",
    "    return corpus_indices, char_to_idx, idx_to_char, vocab_size\n",
    "\n",
    "(corpus_indices, char_to_idx, idx_to_char, vocab_size) = load_data_jay_lyrics()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 时序数据的采样\n",
    "\n",
    "在训练中我们需要每次随机读取小批量样本和标签。与之前章节的实验数据不同的是，时序数据的一个样本通常包含连续的字符。假设时间步数为5，样本序列为5个字符，即“想”“要”“有”“直”“升”。该样本的标签序列为这些字符分别在训练集中的下一个字符，即“要”“有”“直”“升”“机”。我们有两种方式对时序数据进行采样，分别是随机采样和相邻采样。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  随机采样\n",
    "\n",
    "下面的代码每次从数据里随机采样一个小批量。其中批量大小`batch_size`指每个小批量的样本数，`num_steps`为每个样本所包含的时间步数。\n",
    "在随机采样中，每个样本是原始序列上任意截取的一段序列。**相邻的两个随机小批量**在**原始序列上**的位置*不*一定**相毗邻**。因此，我们无法用一个小批量最终时间步的隐藏状态来初始化下一个小批量的隐藏状态。在训练模型时，**每次随机采样前都需要重新初始化隐藏状态**。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [],
   "source": [
    "def data_iter_random(corpus_indices, batch_size, num_steps, device=None):\n",
    "    # 减1是因为输出的索引x是相应输入的索引y加1\n",
    "    num_examples = (len(corpus_indices) - 1) // num_steps\n",
    "    epoch_size = num_examples // batch_size\n",
    "    example_indices = list(range(num_examples))\n",
    "    random.shuffle(example_indices)\n",
    "\n",
    "    # 返回从pos开始的长为num_steps的序列\n",
    "    def _data(pos):\n",
    "        return corpus_indices[pos: pos + num_steps]\n",
    "    if device is None:\n",
    "        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    \n",
    "    for i in range(epoch_size):\n",
    "        # 每次读取batch_size个随机样本\n",
    "        i = i * batch_size\n",
    "        batch_indices = example_indices[i: i + batch_size]\n",
    "        X = [_data(j * num_steps) for j in batch_indices]\n",
    "        Y = [_data(j * num_steps + 1) for j in batch_indices]\n",
    "        yield torch.tensor(X, dtype=torch.float32, device=device), torch.tensor(Y, dtype=torch.float32, device=device)\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 135,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X:  tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],\n",
      "        [12., 13., 14., 15., 16., 17.]], device='cuda:0') \n",
      "Y: tensor([[ 1.,  2.,  3.,  4.,  5.,  6.],\n",
      "        [13., 14., 15., 16., 17., 18.]], device='cuda:0') \n",
      "\n",
      "X:  tensor([[18., 19., 20., 21., 22., 23.],\n",
      "        [ 6.,  7.,  8.,  9., 10., 11.]], device='cuda:0') \n",
      "Y: tensor([[19., 20., 21., 22., 23., 24.],\n",
      "        [ 7.,  8.,  9., 10., 11., 12.]], device='cuda:0') \n",
      "\n"
     ]
    }
   ],
   "source": [
    "my_seq = list(range(30))\n",
    "for X, Y in data_iter_random(my_seq, batch_size=2, num_steps=6):\n",
    "    print('X: ', X, '\\nY:', Y, '\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  相邻采样\n",
    "\n",
    "除对原始序列做随机采样之外，我们还可以**令相邻的两个随机小批量**在**原始序列上的位置相毗邻**。\n",
    "\n",
    "这时候，我们就可以用一个小批量最终时间步的隐藏状态来初始化下一个小批量的隐藏状态，\n",
    "从而使下一个小批量的输出也取决于当前小批量的输入，并如此循环下去。这对实现循环神经网络造成了两方面影响：\n",
    "\n",
    "一方面，在训练模型时，我们只需在**每一个迭代周期开始时初始化隐藏状态**；\n",
    "\n",
    "另一方面，当多个相邻小批量通过传递隐藏状态串联起来时，**模型参数的梯度计算将依赖所有串联起来的小批量序列**。\n",
    "同一迭代周期中，随着迭代次数的增加，**梯度的计算开销会越来越大。**\n",
    "为了使模型参数的梯度计算只依赖一次迭代读取的小批量序列，我们可以在每次读取小批量前将隐藏状态从计算图中分离出来。我们将在下一节（循环神经网络的从零开始实现）的实现中了解这种处理方式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 133,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "def data_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):\n",
    "    if device is None:\n",
    "        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    corpus_indices = torch.tensor(corpus_indices, dtype=torch.float32, device=device)\n",
    "    data_len = len(corpus_indices)\n",
    "    batch_len = data_len // batch_size\n",
    "    indices = corpus_indices[0: batch_size*batch_len].view(batch_size, batch_len)\n",
    "    epoch_size = (batch_len - 1) // num_steps\n",
    "    for i in range(epoch_size):\n",
    "        i = i * num_steps\n",
    "        X = indices[:, i: i + num_steps]\n",
    "        Y = indices[:, i + 1: i + num_steps + 1]\n",
    "        yield X, Y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([[ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12.],\n",
      "        [13., 14., 15., 16., 17., 18., 19., 20., 21., 22., 23., 24., 25.],\n",
      "        [26., 27., 28., 29., 30., 31., 32., 33., 34., 35., 36., 37., 38.]],\n",
      "       device='cuda:0')\n",
      "tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],\n",
      "        [13., 14., 15., 16., 17., 18.],\n",
      "        [26., 27., 28., 29., 30., 31.]], device='cuda:0')\n",
      "X:  tensor([[ 0.,  1.,  2.,  3.,  4.,  5.],\n",
      "        [13., 14., 15., 16., 17., 18.],\n",
      "        [26., 27., 28., 29., 30., 31.]], device='cuda:0') \n",
      "Y: tensor([[ 1.,  2.,  3.,  4.,  5.,  6.],\n",
      "        [14., 15., 16., 17., 18., 19.],\n",
      "        [27., 28., 29., 30., 31., 32.]], device='cuda:0') \n",
      "\n",
      "tensor([[ 6.,  7.,  8.,  9., 10., 11.],\n",
      "        [19., 20., 21., 22., 23., 24.],\n",
      "        [32., 33., 34., 35., 36., 37.]], device='cuda:0')\n",
      "X:  tensor([[ 6.,  7.,  8.,  9., 10., 11.],\n",
      "        [19., 20., 21., 22., 23., 24.],\n",
      "        [32., 33., 34., 35., 36., 37.]], device='cuda:0') \n",
      "Y: tensor([[ 7.,  8.,  9., 10., 11., 12.],\n",
      "        [20., 21., 22., 23., 24., 25.],\n",
      "        [33., 34., 35., 36., 37., 38.]], device='cuda:0') \n",
      "\n"
     ]
    }
   ],
   "source": [
    "my_seq = list(range(40))\n",
    "\n",
    "# corpus_indices, char_to_idx, idx_to_char, vocab_size = load_data_jay_lyrics()    \n",
    "\n",
    "# for X, Y in data_iter_consecutive(corpus_indices, 12, 10, device):\n",
    "#     print(X.shape)\n",
    "\n",
    "for X, Y in data_iter_consecutive(my_seq, batch_size=3, num_steps=6):\n",
    "    print('X: ', X, '\\nY:', Y, '\\n')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 循环神经网络的从零开始实现\n",
    "\n",
    "在本节中，我们将从零开始实现一个基于字符级循环神经网络的语言模型，并在周杰伦专辑歌词数据集上训练一个模型来进行歌词创作。首先，我们读取周杰伦专辑歌词数据集："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "(corpus_indices, char_to_idx, idx_to_char, vocab_size) = load_data_jay_lyrics()    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## one-hot向量\n",
    "\n",
    "为了将词表示成向量输入到神经网络，一个简单的办法是使用one-hot向量。假设词典中不同字符的数量为$N$（即词典大小`vocab_size`），每个字符已经同一个从0到$N-1$的连续整数值索引一一对应。如果一个字符的索引是整数$i$, 那么我们创建一个全0的长为$N$的向量，并将其位置为$i$的元素设成1。该向量就是对原字符的one-hot向量。下面分别展示了索引为0和2的one-hot向量，向量长度等于词典大小。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## RNN的输入\n",
    "\n",
    "### 对于文本信息来说，一般文本中的“词” word 被构造为词向量，而这里汉字被构造为 One-hot向量。\n",
    "\n",
    "用一个010000的独热编码来表示一个词库中的一个元素，来对文字信息进行编码。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch.nn.functional as F\n",
    "import numpy as np\n",
    "\n",
    "X = torch.arange(10).view(2, 5)\n",
    "onet_hot_features = F.one_hot(X, torch.tensor(10))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([2, 5, 10])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "onet_hot_features.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们每次采样的小批量的形状是(批量大小, 时间步数)。下面的函数将这样的小批量变换成数个可以输入进网络的形状为(批量大小, 词典大小)的矩阵，矩阵个数等于时间步数。也就是说，时间步$t$的输入为$\\boldsymbol{X}_t \\in \\mathbb{R}^{n \\times d}$，其中$n$为批量大小，$d$为输入个数，即one-hot向量长度（词典大小）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 217,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "d:\\python36\\lib\\site-packages\\ipykernel_launcher.py:25: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X:  tensor([[0., 1., 2.],\n",
      "        [5., 6., 7.]], device='cuda:0') \n",
      "Y: tensor([[1., 2., 3.],\n",
      "        [6., 7., 8.]], device='cuda:0') \n",
      "\n",
      "3 [tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]], device='cuda:0'), tensor([[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]], device='cuda:0'), tensor([[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]], device='cuda:0')] torch.Size([2, 10])\n",
      "3 [tensor([[1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 1., 0., 0., 0., 0.]], device='cuda:0'), tensor([[0., 1., 0., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]], device='cuda:0'), tensor([[0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],\n",
      "        [0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]], device='cuda:0')] torch.Size([2, 10])\n",
      "True\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "def one_hot(x, n_class, dtype=torch.float32): \n",
    "    # X shape: (batch), output shape: (batch, n_class)\n",
    "    x = x.long()\n",
    "    res = torch.zeros(x.shape[0], n_class, dtype=dtype, device=x.device)\n",
    "    res.scatter_(1, x.view(-1, 1), 1)\n",
    "    return res\n",
    "\n",
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "def to_onehot(X, n_class):  \n",
    "    # X shape: (batch, seq_len), output: seq_len elements of (batch, n_class)\n",
    "    return [one_hot(X[:, i], n_class) for i in range(X.shape[1])]\n",
    "\n",
    "\n",
    "import torch.nn.functional as F\n",
    "\n",
    "def to_one_hot(X, n_class):\n",
    "    assert isinstance(X, torch.Tensor)\n",
    "    X.to('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    #print(X[:, 0].long())\n",
    "    return [F.one_hot(X[:, i].long(), n_class ).float() for i in range(X.shape[1])]\n",
    "    \n",
    "def mydata_iter_consecutive(corpus_indices, batch_size, num_steps, device=None):\n",
    "    if device is None:\n",
    "        device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
    "    corpus_indices = torch.tensor(corpus_indices, dtype=torch.float32, device=device)\n",
    "    data_len = len(corpus_indices)\n",
    "    batch_len = data_len // batch_size\n",
    "    indices = corpus_indices[0: batch_size*batch_len].view(batch_size, batch_len)\n",
    "    epoch_size = (batch_len - 1) // num_steps\n",
    "    for i in range(epoch_size):\n",
    "        i = i * num_steps\n",
    "        X = indices[:, i: i + num_steps]\n",
    "        Y = indices[:, i + 1: i + num_steps + 1]\n",
    "        yield X, Y\n",
    "        \n",
    "\n",
    "\n",
    "#X = torch.arange(10).view(2, 5)\n",
    "class_num  = 10\n",
    "my_seq = torch.tensor(list(range(class_num)))\n",
    "for X, Y in mydata_iter_consecutive(my_seq, batch_size=2, num_steps=3):\n",
    "    print('X: ', X, '\\nY:', Y, '\\n')\n",
    "    \n",
    "    one_hot_inputs = to_one_hot(X, class_num)\n",
    "    \n",
    "    inputs = to_onehot(X, class_num)\n",
    "\n",
    "    print(len(inputs), inputs , inputs[0].shape)\n",
    "    print(len(one_hot_inputs), one_hot_inputs , one_hot_inputs[0].shape)\n",
    "    \n",
    "    for i in range(len(inputs)):\n",
    "        print(torch.equal(inputs[i], one_hot_inputs[i]))\n",
    "        "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 147,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([6, 5])\n"
     ]
    }
   ],
   "source": [
    "out = (torch.randn(3,5),torch.randn(3,5))\n",
    "out = torch.cat(out, dim = 0)\n",
    "print(out.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 初始化模型参数\n",
    "\n",
    "接下来，我们初始化模型参数。隐藏单元个数 `num_hiddens`是一个超参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 205,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "will use cuda\n"
     ]
    }
   ],
   "source": [
    "num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size\n",
    "print('will use', device)\n",
    "\n",
    "def get_params():\n",
    "    def _one(shape):\n",
    "        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)\n",
    "        return torch.nn.Parameter(ts, requires_grad=True)\n",
    "\n",
    "    # 隐藏层参数\n",
    "    W_xh = _one((num_inputs, num_hiddens))\n",
    "    W_hh = _one((num_hiddens, num_hiddens))\n",
    "    b_h = torch.nn.Parameter(torch.zeros(num_hiddens, device=device, requires_grad=True))\n",
    "    # 输出层参数\n",
    "    W_hq = _one((num_hiddens, num_outputs))\n",
    "    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, requires_grad=True))\n",
    "    return nn.ParameterList([W_xh, W_hh, b_h, W_hq, b_q])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型\n",
    "\n",
    "我们根据循环神经网络的计算表达式实现该模型。首先定义`init_rnn_state`函数来返回初始化的隐藏状态。它返回由一个形状为(批量大小, 隐藏单元个数)的值为0的`NDArray`组成的元组。使用元组是为了更便于处理隐藏状态含有多个`NDArray`的情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 206,
   "metadata": {},
   "outputs": [],
   "source": [
    "def init_rnn_state(batch_size, num_hiddens, device):\n",
    "    return (torch.zeros((batch_size, num_hiddens), device=device), )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "下面的`rnn`函数定义了在一个时间步里如何计算隐藏状态和输出。这里的激活函数使用了tanh函数。3.8节（多层感知机）中介绍过，当元素在实数域上均匀分布时，tanh函数值的均值为0。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 207,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rnn(inputs, state, params):\n",
    "    # inputs和outputs皆为num_steps个形状为(batch_size, vocab_size)的矩阵\n",
    "    W_xh, W_hh, b_h, W_hq, b_q = params\n",
    "    H, = state\n",
    "    outputs = []\n",
    "\n",
    "    for X in inputs:\n",
    "        H = torch.tanh(torch.matmul(X, W_xh) + torch.matmul(H, W_hh) + b_h)\n",
    "        Y = torch.matmul(H, W_hq) + b_q\n",
    "        outputs.append(Y)\n",
    "        \n",
    "    return outputs, (H,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 怎么理解RNN的输入 \n",
    "\n",
    "## Num_step\n",
    "\n",
    "一段序列中，具有多少个词，例如将num_steps设置为4，则采样的是以4个词作为步长，\n",
    "例如“是不是傻”， 这个可以看作是一条数据。\n",
    "\n",
    "## one_hot_size\n",
    "“是不是傻” 这是文字，需要用词库编码为one-hot向量，或者其他词向量等形式，\n",
    "那样一个词就被映射为[vocab_size]大小的向量。\n",
    "\n",
    "对于一个长度为： num_step的数据， 一条数据大小就有[num_step, vocab_size]\n",
    "## batch_size\n",
    "只用一条数据训练显然是不行的，所以要引入一个batch,而这里的batch如何理解，例如batch_size为3：\n",
    "\n",
    "“是不是傻啊”\n",
    "\n",
    "“你是傻是吗” \n",
    "\n",
    "\n",
    "\n",
    "这样构成的数据大小为**\\[batch_size, num_step, vocab_size\\] = 【2条数据， 每条5个词， 每个词one-hot 1027维】**\n",
    "\n",
    "但是我们训练的时候都是二维的矩阵相乘，这里回到RNN的原始概念，利用上一层隐含层的输出，结合当前状态的输入，来预测下一个：“可能的数据”\n",
    "\n",
    "所以输入的数据应该是 **\\[batch_size, vocab_size\\]=【2条数据， 1027维度】**，5个词 num_steps维度作为隐状态迭代次数。\n",
    "\n",
    "例如：\n",
    "\n",
    "将“是”“你”  的独热向量作为输入，而其输出, 而num_steps 有5个，所以会通过这进行num_steps次状态迭代，得到最终预测的一组输出，而隐含层输出也可以记录下来，所以会有5 条**\\[batch_size , vocab_size\\]** 大小的数据，每个数据是预测的下一个词的独热编码。\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 218,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[tensor([[1., 0., 0.,  ..., 0., 0., 0.],\n",
      "        [0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0'), tensor([[0., 1., 0.,  ..., 0., 0., 0.],\n",
      "        [0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0'), tensor([[0., 0., 1.,  ..., 0., 0., 0.],\n",
      "        [0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0'), tensor([[0., 0., 0.,  ..., 0., 0., 0.],\n",
      "        [0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0'), tensor([[0., 0., 0.,  ..., 0., 0., 0.],\n",
      "        [0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "5 torch.Size([2, 1027]) torch.Size([2, 256])\n"
     ]
    }
   ],
   "source": [
    "# import torch.nn as nn\n",
    "# def to_onehot(X, vocab_size):\n",
    "#     return F.one_hot((X.view(X.shape[1],-1)).to(device), vocab_size)\n",
    "\n",
    "state = init_rnn_state(X.shape[0], num_hiddens, device)\n",
    "\n",
    "#inputs = to_onehot(X.to(device), 10)\n",
    "X = torch.arange(10).view(2, 5).to('cuda')\n",
    "\n",
    "#onet_hot_features = F.one_hot(X.to(device), vocab_size)\n",
    "\n",
    "inputs = to_one_hot(X,vocab_size)\n",
    "print(inputs)\n",
    "params = get_params()\n",
    "\n",
    "\n",
    "outputs, state_new = rnn([data.float() for data in inputs], state, params)\n",
    "\n",
    "print(len(outputs), outputs[0].shape, state_new[0].shape) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  定义预测函数\n",
    "\n",
    "以下函数基于前缀`prefix`（含有数个字符的字符串）来预测接下来的`num_chars`个字符。这个函数稍显复杂，其中我们将循环神经单元`rnn`设置成了函数参数，这样在后面小节介绍其他循环神经网络时能重复使用这个函数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 224,
   "metadata": {},
   "outputs": [],
   "source": [
    "def predict_rnn(prefix, num_chars, rnn, params, init_rnn_state,\n",
    "                num_hiddens, vocab_size, device, idx_to_char, char_to_idx):\n",
    "    state = init_rnn_state(1, num_hiddens, device)\n",
    "    output = [char_to_idx[prefix[0]]]\n",
    "    #print(output)\n",
    "    for t in range(num_chars + len(prefix) - 1):\n",
    "        #print('pre fix {}'.format(t))\n",
    "        # 将上一时间步的输出作为当前时间步的输入\n",
    "        #print('input is {}'.format(idx_to_char[output[-1]]))\n",
    "        X = to_one_hot(torch.tensor([[output[-1]]], device=device), vocab_size)\n",
    "        #print(X)\n",
    "        # 计算输出和更新隐藏状态\n",
    "        (Y, state) = rnn(X, state, params)\n",
    "        \n",
    "        # 下一个时间步的输入是prefix里的字符或者当前的最佳预测字符\n",
    "        if t < len(prefix) - 1:\n",
    "            output.append(char_to_idx[prefix[t + 1]])\n",
    "        else:\n",
    "            output.append(int(Y[0].argmax(dim=1).item()))\n",
    "            \n",
    "    return ''.join([idx_to_char[i] for i in output])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们先测试一下`predict_rnn`函数。我们将根据前缀“分开”创作长度为10个字符（不考虑前缀长度）的一段歌词。因为模型参数为随机值，所以预测结果也是随机的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 225,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 便\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 朽\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 蝙\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 险\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 钩\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 题\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 啊\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 啸\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 杵\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'分开便朽蝙险钩题啊啸杵山'"
      ]
     },
     "execution_count": 225,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "predict_rnn('分开', 10, rnn, params, init_rnn_state, num_hiddens, vocab_size,\n",
    "            device, idx_to_char, char_to_idx)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 裁剪梯度\n",
    "\n",
    "循环神经网络中较容易出现梯度衰减或梯度爆炸。我们会在6.6节（通过时间反向传播）中解释原因。为了应对梯度爆炸，我们可以裁剪梯度（clip gradient）。假设我们把所有模型参数梯度的元素拼接成一个向量 $\\boldsymbol{g}$，并设裁剪的阈值是$\\theta$。裁剪后的梯度\n",
    "\n",
    "$$ \\min\\left(\\frac{\\theta}{\\|\\boldsymbol{g}\\|}, 1\\right)\\boldsymbol{g}$$\n",
    "\n",
    "的$L_2$范数不超过$\\theta$。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 69,
   "metadata": {},
   "outputs": [],
   "source": [
    "def grad_clipping(params, theta, device):\n",
    "    norm = torch.tensor([0.0], device=device)\n",
    "    for param in params:\n",
    "        norm += (param.grad.data ** 2).sum()\n",
    "    norm = norm.sqrt().item()\n",
    "    if norm > theta:\n",
    "        for param in params:\n",
    "            param.grad.data *= (theta / norm)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  困惑度\n",
    "\n",
    "我们通常使用困惑度（perplexity）来评价语言模型的好坏。回忆一下3.4节（softmax回归）中交叉熵损失函数的定义。困惑度是对交叉熵损失函数做指数运算后得到的值。特别地，\n",
    "\n",
    "- 最佳情况下，模型总是把标签类别的概率预测为1，此时困惑度为1；\n",
    "- 最坏情况下，模型总是把标签类别的概率预测为0，此时困惑度为正无穷；\n",
    "- 基线情况下，模型总是预测所有类别的概率都相同，此时困惑度为类别个数。\n",
    "\n",
    "显然，任何一个有效模型的困惑度必须小于类别个数。在本例中，困惑度必须小于词典大小`vocab_size`。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型训练函数\n",
    "\n",
    "跟之前章节的模型训练函数相比，这里的模型训练函数有以下几点不同：\n",
    "\n",
    "1. 使用困惑度评价模型。\n",
    "2. 在迭代模型参数前裁剪梯度。\n",
    "3. 对时序数据采用不同采样方法将导致隐藏状态初始化的不同。相关讨论可参考6.3节（语言模型数据集（周杰伦专辑歌词））。\n",
    "\n",
    "另外，考虑到后面将介绍的其他循环神经网络，为了更通用，这里的函数实现更长一些。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 226,
   "metadata": {},
   "outputs": [],
   "source": [
    "import time\n",
    "import dl_utils\n",
    "import math\n",
    "def train_and_predict_rnn(rnn, get_params, init_rnn_state, num_hiddens,\n",
    "                          vocab_size, device, corpus_indices, idx_to_char,\n",
    "                          char_to_idx, is_random_iter, num_epochs, num_steps,\n",
    "                          lr, clipping_theta, batch_size, pred_period,\n",
    "                          pred_len, prefixes):\n",
    "    if is_random_iter:\n",
    "        data_iter_fn = data_iter_random\n",
    "    else:\n",
    "        data_iter_fn = data_iter_consecutive\n",
    "    params = get_params()\n",
    "    loss = nn.CrossEntropyLoss()\n",
    "\n",
    "    for epoch in range(num_epochs):\n",
    "        if not is_random_iter:  # 如使用相邻采样，在epoch开始时初始化隐藏状态\n",
    "            state = init_rnn_state(batch_size, num_hiddens, device)\n",
    "        l_sum, n, start = 0.0, 0, time.time()\n",
    "        data_iter = data_iter_fn(corpus_indices, batch_size, num_steps, device)\n",
    "        for X, Y in data_iter:\n",
    "            if is_random_iter:  # 如使用随机采样，在每个小批量更新前初始化隐藏状态\n",
    "                state = init_rnn_state(batch_size, num_hiddens, device)\n",
    "            else:  \n",
    "            # 否则需要使用detach函数从计算图分离隐藏状态, 这是为了\n",
    "            # 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)\n",
    "                for s in state:\n",
    "                    s.detach_()\n",
    "            #X = torch.Tensor(X)\n",
    "            #print(x.device)\n",
    "            inputs = to_one_hot(X, vocab_size)\n",
    "            # outputs有num_steps个形状为(batch_size, vocab_size)的矩阵\n",
    "            (outputs, state) = rnn(inputs, state, params)\n",
    "            \n",
    "            # 拼接之后形状为(num_steps * batch_size, vocab_size)\n",
    "            \n",
    "            outputs = torch.cat(outputs, dim=0)\n",
    "            \n",
    "            # Y的形状是(batch_size, num_steps)，转置后再变成长度为\n",
    "            # batch * num_steps 的向量，这样跟输出的行一一对应\n",
    "            \n",
    "            y = torch.transpose(Y, 0, 1).contiguous().view(-1)\n",
    "            # 使用交叉熵损失计算平均分类误差\n",
    "            l = loss(outputs, y.long())\n",
    "            \n",
    "            # 梯度清0\n",
    "            if params[0].grad is not None:\n",
    "                for param in params:\n",
    "                    param.grad.data.zero_()\n",
    "            l.backward()\n",
    "            grad_clipping(params, clipping_theta, device)  # 裁剪梯度\n",
    "            dl_utils.sgd(params, lr, 1)  # 因为误差已经取过均值，梯度不用再做平均\n",
    "            \n",
    "            l_sum += l.item() * y.shape[0]\n",
    "            n += y.shape[0]\n",
    "\n",
    "        if (epoch + 1) % pred_period == 0:\n",
    "            print('epoch %d, perplexity %f, time %.2f sec' % (\n",
    "                epoch + 1, math.exp(l_sum / n), time.time() - start))\n",
    "            for prefix in prefixes:\n",
    "                print(' -', predict_rnn(prefix, pred_len, rnn, params, init_rnn_state,\n",
    "                    num_hiddens, vocab_size, device, idx_to_char, char_to_idx))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练模型并创作歌词\n",
    "\n",
    "现在我们可以训练模型了。首先，设置模型超参数。我们将根据前缀“分开”和“不分开”分别创作长度为50个字符（不考虑前缀长度）的一段歌词。我们每过50个迭代周期便根据当前训练的模型创作一段歌词。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 227,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_epochs, num_steps, batch_size, lr, clipping_theta = 250, 35, 32, 1e2, 1e-2\n",
    "pred_period, pred_len, prefixes = 50, 50, ['分开', '不分开']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 228,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 50, perplexity 70.646543, time 0.48 sec\n",
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 别\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 分开 我不要再想 我不要 想想我 别子我 别子我 别子我 别子我 别子我 别子我 别子我 别子我 别子我\n",
      "[175]\n",
      "pre fix 0\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 外\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 起\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 起\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 起\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 51\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 不分开 我想要你 你我不外 我不要  爱我不起 我不要  爱我不起 我不要  爱我不起 我不要  爱我不起\n",
      "epoch 100, perplexity 10.035940, time 0.49 sec\n",
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 有\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 伦\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 人\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 留\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 在\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 直\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 全\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 小\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 完\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 外\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 溪\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 边\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 默\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 店\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 小\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 二\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 三\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 两\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 银\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 够\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 够\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 景\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 色\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 哈\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 兮\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 该\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 了\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 这\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 生\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 有\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 烦\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 分开 有一伦人留 你在一直 全小完外的溪边 我默店小二 三两银够不够 景色哈兮 我该了这生  我有你烦 \n",
      "[175]\n",
      "pre fix 0\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 堡\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 不\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 51\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 不分开堡 我不要再想 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 \n",
      "epoch 150, perplexity 2.962483, time 0.48 sec\n",
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 有\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 定\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 在\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 废\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 鸠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 白\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 随\n",
      "[tensor([[0., 0., 0.,  ..., 1., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 时\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 准\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 备\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 重\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 袭\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 怀\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 念\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 起\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 国\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 小\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 课\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 都\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 丘\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 站\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 地\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 只\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 饿\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 昏\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 老\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 斑\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 鸠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 印\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 地\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 安\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 老\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 斑\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 鸠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 腿\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 短\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 毛\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 分开 有一定在废鸠的白 随时准备重袭 我怀念起国小的课都丘 站地一只饿昏的老斑鸠 印地安老斑鸠 腿短毛不\n",
      "[175]\n",
      "pre fix 0\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 期\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 能\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 能\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 要\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 想\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 知\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 觉\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 已\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 经\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 离\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 知\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 觉\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 跟\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 了\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 这\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 51\n",
      "input is 节\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 不分开期 我不能再  我不能再想 我不 我不 我不要再想你 不知不觉 你已经离开我 不知不觉 我跟了这节奏\n",
      "epoch 200, perplexity 1.633108, time 0.51 sec\n",
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 快\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 使\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 用\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 截\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 静\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 慢\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 温\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 习\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 让\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 怕\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 上\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 那\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 场\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 悲\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 剧\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 是\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 完\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 美\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 演\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 出\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 场\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 戏\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 宁\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 愿\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 心\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 碎\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 哭\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 泣\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 狠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 狠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 忘\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 记\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 分开 快使用不截 静慢温习 让我怕上你 那场悲剧 是你完美演出的一场戏 宁愿心碎哭泣 再狠狠忘记 你爱过\n",
      "[175]\n",
      "pre fix 0\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 期\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 叫\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 爸\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 打\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 妈\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 这\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 样\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 对\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 吗\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 干\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 嘛\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 这\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 样\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 何\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 必\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 让\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 酒\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 牵\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 鼻\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 子\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 走\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is  \n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 瞎\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 说\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 底\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 了\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 打\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 阳\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 光\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 言\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is 主\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 中\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 格\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 错\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 翰\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 福\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 音\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 51\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 不分开期 我叫你爸 你打我妈 这样对吗干嘛这样 何必让酒牵鼻子走 瞎 说底了打阳光 一言主中格错翰福音 像\n",
      "epoch 250, perplexity 1.293136, time 0.46 sec\n",
      "[948]\n",
      "pre fix 0\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 球\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 日\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 落\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 斑\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 还\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 无\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is 仔\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 躺\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 斑\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 鸠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is 枯\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 牛\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 过\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 秃\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is 鹰\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 盘\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 旋\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 死\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 盯\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is 着\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 腐\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 肉\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 草\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 原\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 上\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 两\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 只\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 敌\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 对\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is 野\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 牛\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 在\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 远\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 方\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 决\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 斗\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 在\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 处\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 被\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 废\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is 弃\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 分开球日的落斑还 无仔的躺斑鸠枯的牛过 秃鹰盘旋死盯着腐肉 草原上两只敌对野牛在远方决斗 在一处被废弃的\n",
      "[175]\n",
      "pre fix 0\n",
      "input is 不\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 1\n",
      "input is 分\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 2\n",
      "input is 开\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 3\n",
      "input is 期\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 4\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 5\n",
      "input is 然\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 6\n",
      "input is 后\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 7\n",
      "input is 将\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 8\n",
      "input is 过\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 9\n",
      "input is 去\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 10\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 11\n",
      "input is 慢\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 12\n",
      "input is 慢\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 13\n",
      "input is 温\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 14\n",
      "input is 习\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 15\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 16\n",
      "input is 让\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 17\n",
      "input is 我\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 18\n",
      "input is 爱\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 19\n",
      "input is 上\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 20\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 21\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 22\n",
      "input is 那\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 23\n",
      "input is 场\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 24\n",
      "input is 悲\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 25\n",
      "input is 剧\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 26\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 27\n",
      "input is 是\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 28\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 29\n",
      "input is 完\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 30\n",
      "input is 美\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 31\n",
      "input is 演\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 32\n",
      "input is 出\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 33\n",
      "input is 的\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 34\n",
      "input is 一\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 35\n",
      "input is 场\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 36\n",
      "input is 戏\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 37\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 38\n",
      "input is 宁\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 39\n",
      "input is 愿\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 40\n",
      "input is 心\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 41\n",
      "input is 碎\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 42\n",
      "input is 哭\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 43\n",
      "input is 泣\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 44\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 45\n",
      "input is 再\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 46\n",
      "input is 狠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 47\n",
      "input is 狠\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 48\n",
      "input is 忘\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 49\n",
      "input is 记\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 50\n",
      "input is  \n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      "pre fix 51\n",
      "input is 你\n",
      "[tensor([[0., 0., 0.,  ..., 0., 0., 0.]], device='cuda:0')]\n",
      " - 不分开期 然后将过去 慢慢温习 让我爱上你 那场悲剧 是你完美演出的一场戏 宁愿心碎哭泣 再狠狠忘记 你爱\n"
     ]
    }
   ],
   "source": [
    "import torch.nn as nn\n",
    "def one_hot(x, n_class, dtype=torch.float32): \n",
    "    # X shape: (batch), output shape: (batch, n_class)\n",
    "    x = x.long()\n",
    "    res = torch.zeros(x.shape[0], n_class, dtype=dtype, device=x.device)\n",
    "    res.scatter_(1, x.view(-1, 1), 1)\n",
    "    return res\n",
    "\n",
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "def to_onehot(X, n_class):  \n",
    "    # X shape: (batch, seq_len), output: seq_len elements of (batch, n_class)\n",
    "    return [one_hot(X[:, i], n_class) for i in range(X.shape[1])]\n",
    "\n",
    "train_and_predict_rnn(rnn, get_params, init_rnn_state, num_hiddens,\n",
    "                      vocab_size, device, corpus_indices, idx_to_char,\n",
    "                      char_to_idx, True, num_epochs, num_steps, lr,\n",
    "                      clipping_theta, batch_size, pred_period, pred_len,\n",
    "                      prefixes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 134,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 50, perplexity 64.308868, time 0.35 sec\n",
      " - 分开 我想要这想 我不要再不 我不要再想 我不要再不 我不要再想 我不要再不 我不要再想 我不要再不 我\n",
      " - 不分开 我想要这 我有了双截 谁不的美 在子我的见 我不要你 你有我有 我想我这多 是不的没不 我不要再想\n",
      "epoch 100, perplexity 7.124937, time 0.35 sec\n",
      " - 分开 我不要这样瘦着你的姑不 我的星写 你来了一凉 银制茶烛 温暖了外屋 白制蜡烛 温暖了空屋 白色蜡烛\n",
      " - 不分开觉 你样经离开我 后知不觉 我该了这节活 后知后觉 我该了这节活 后知后觉 我该了好生活 后知后觉 \n",
      "epoch 150, perplexity 2.044541, time 0.34 sec\n",
      " - 分开 我不要再想瘦 你爱你的天 我在魂球 在水怕空的 白有蜡在 全我了空的溪边河口默默等著我 娘子依旧每\n",
      " - 不分开觉你 我以多你想离 如果我遇见你是一场悲剧 我想我这辈子注定一个人演戏 最后再一个人慢慢的回忆 没有\n",
      "epoch 200, perplexity 1.303947, time 0.35 sec\n",
      " - 分开 她候我 谁不让没我手 你说啊 是不是你不想活 说你怎么面对我 甩开球我满腔的怒火 我想揍你已经很久\n",
      " - 不分开觉 你样经离 你去再蒙 怎么作痛 你都没痛 说我不懂 说了没用 他的笑容 有何不同 在你心中 我不再\n",
      "epoch 250, perplexity 1.165118, time 0.34 sec\n",
      " - 分开 她候的 旧时么 装满了明信代的铁盒里藏著一片玫瑰花瓣 黄金葛爬满了雕花的门窗 夕阳斜斜映在斑驳的砖\n",
      " - 不分开觉你 我爱要你想经 不果我遇见 一什是我感无 它下妙传出手 漂亮的假动作 帅呆了我 说你说 分数怎么\n"
     ]
    }
   ],
   "source": [
    "train_and_predict_rnn(rnn, get_params, init_rnn_state, num_hiddens,\n",
    "                      vocab_size, device, corpus_indices, idx_to_char,\n",
    "                      char_to_idx, False, num_epochs, num_steps, lr,\n",
    "                      clipping_theta, batch_size, pred_period, pred_len,\n",
    "                      prefixes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Pytorch 循环神经网络的简洁实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 148,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([35, 2, 256]) 1 torch.Size([2, 256])\n"
     ]
    }
   ],
   "source": [
    "\n",
    "num_hiddens = 256\n",
    "# rnn_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens) # 已测试\n",
    "rnn_layer = nn.RNN(input_size=vocab_size, hidden_size=num_hiddens)\n",
    "\n",
    "num_steps = 35\n",
    "batch_size = 2\n",
    "state = None\n",
    "X = torch.rand(num_steps, batch_size, vocab_size)\n",
    "Y, state_new = rnn_layer(X, state)\n",
    "print(Y.shape, len(state_new), state_new[0].shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 152,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RNNModel(nn.Module):\n",
    "    def __init__(self, rnn_layer, vocab_size):\n",
    "        super(RNNModel, self).__init__()\n",
    "        self.rnn = rnn_layer\n",
    "        self.hidden_size = rnn_layer.hidden_size * (2 if rnn_layer.bidirectional else 1) \n",
    "        self.vocab_size = vocab_size\n",
    "        self.dense = nn.Linear(self.hidden_size, vocab_size)\n",
    "        self.state = None\n",
    "\n",
    "    def forward(self, inputs, state): # inputs: (batch, seq_len)\n",
    "        # 获取one-hot向量表示\n",
    "        X = to_onehot(inputs, vocab_size) # X是个list\n",
    "        Y, self.state = self.rnn(torch.stack(X), state)\n",
    "        # 全连接层会首先将Y的形状变成(num_steps * batch_size, num_hiddens)，它的输出\n",
    "        # 形状为(num_steps * batch_size, vocab_size)\n",
    "        output = self.dense(Y.view(-1, Y.shape[-1]))\n",
    "        return output, self.state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 本函数已保存在d2lzh_pytorch包中方便以后使用\n",
    "def predict_rnn_pytorch(prefix, num_chars, model, vocab_size, device, idx_to_char,\n",
    "                      char_to_idx):\n",
    "    state = None\n",
    "    output = [char_to_idx[prefix[0]]] # output会记录prefix加上输出\n",
    "    for t in range(num_chars + len(prefix) - 1):\n",
    "        X = torch.tensor([output[-1]], device=device).view(1, 1)\n",
    "        if state is not None:\n",
    "            if isinstance(state, tuple): # LSTM, state:(h, c)  \n",
    "                state = (state[0].to(device), state[1].to(device))\n",
    "            else:   \n",
    "                state = state.to(device)\n",
    "            \n",
    "        (Y, state) = model(X, state)  # 前向计算不需要传入模型参数\n",
    "        if t < len(prefix) - 1:\n",
    "            output.append(char_to_idx[prefix[t + 1]])\n",
    "        else:\n",
    "            output.append(int(Y.argmax(dim=1).item()))\n",
    "    return ''.join([idx_to_char[i] for i in output])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 154,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'分开箭太泽跑泽泽泽泽泽泽'"
      ]
     },
     "execution_count": 154,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "model = RNNModel(rnn_layer, vocab_size).to(device)\n",
    "predict_rnn_pytorch('分开', 10, model, vocab_size, device, idx_to_char, char_to_idx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 159,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,\n",
    "                                corpus_indices, idx_to_char, char_to_idx,\n",
    "                                num_epochs, num_steps, lr, clipping_theta,\n",
    "                                batch_size, pred_period, pred_len, prefixes):\n",
    "    loss = nn.CrossEntropyLoss()\n",
    "    optimizer = torch.optim.Adam(model.parameters(), lr=lr)\n",
    "    model.to(device)\n",
    "    state = None\n",
    "    for epoch in range(num_epochs):\n",
    "        l_sum, n, start = 0.0, 0, time.time()\n",
    "        data_iter = data_iter_consecutive(corpus_indices, batch_size, num_steps, device) # 相邻采样\n",
    "        for X, Y in data_iter:\n",
    "            if state is not None:\n",
    "                # 使用detach函数从计算图分离隐藏状态, 这是为了\n",
    "                # 使模型参数的梯度计算只依赖一次迭代读取的小批量序列(防止梯度计算开销太大)\n",
    "                if isinstance (state, tuple): # LSTM, state:(h, c)  \n",
    "                    state = (state[0].detach(), state[1].detach())\n",
    "                else:   \n",
    "                    state = state.detach()\n",
    "    \n",
    "            (output, state) = model(X, state) # output: 形状为(num_steps * batch_size, vocab_size)\n",
    "            \n",
    "            # Y的形状是(batch_size, num_steps)，转置后再变成长度为\n",
    "            # batch * num_steps 的向量，这样跟输出的行一一对应\n",
    "            y = torch.transpose(Y, 0, 1).contiguous().view(-1)\n",
    "            l = loss(output, y.long())\n",
    "            \n",
    "            optimizer.zero_grad()\n",
    "            l.backward()\n",
    "            # 梯度裁剪\n",
    "            grad_clipping(model.parameters(), clipping_theta, device)\n",
    "            optimizer.step()\n",
    "            l_sum += l.item() * y.shape[0]\n",
    "            n += y.shape[0]\n",
    "        \n",
    "        try:\n",
    "            perplexity = math.exp(l_sum / n)\n",
    "        except OverflowError:\n",
    "            perplexity = float('inf')\n",
    "        if (epoch + 1) % pred_period == 0:\n",
    "            print('epoch %d, perplexity %f, time %.2f sec' % (\n",
    "                epoch + 1, perplexity, time.time() - start))\n",
    "            for prefix in prefixes:\n",
    "                print(' -', predict_rnn_pytorch(\n",
    "                    prefix, pred_len, model, vocab_size, device, idx_to_char,\n",
    "                    char_to_idx))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 50, perplexity 10.323663, time 0.09 sec\n",
      " - 分开始不起 爱你在我 你的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我疯狂的\n",
      " - 不分开  一场悲剧 我想我这样你的手 一不的口  我不要 你想我这样你的人  不知道  我不了 不你我想 \n",
      "epoch 100, perplexity 1.278276, time 0.09 sec\n",
      " - 分开 一起心动的黑色幽默 你的完美主义 太彻底 让我连恨都难以下笔 将真心抽离写成日记 像是一场默剧 你\n",
      " - 不分开始 我不懂 爱你在练 我有你爸 你笑手 一定 我感到的可爱女人 坏坏的让我疯狂的可爱女人 坏坏的让我\n",
      "epoch 150, perplexity 1.063667, time 0.10 sec\n",
      " - 分开 一起心中 在 已经过人雨 风沉默没开  我上没有你 相思 说 泪怎么空 我都说的 从小到真的在我吸\n",
      " - 不分开不了我不能再想 我不 再不 我不能 爱情走的太快就像龙卷风 不能承受我已无处可躲 我不要再想 我不要\n",
      "epoch 200, perplexity 1.031684, time 0.10 sec\n",
      " - 分开 一起心中 在 有神个人让后知后觉 我该好好生活 我该好好生活 不知不觉 你已经离开我 不知不觉 我\n",
      " - 不分开不了我不能 说你怎么面对我 甩开球 满腔的怒火 我想揍你已经很久 别想躲 说你眼睛看着我 别发抖 快\n",
      "epoch 250, perplexity 1.058628, time 0.11 sec\n",
      " - 分开 一起心中 夕阳有钟映在斑驳的砖墙 铺著榉木板的屋内还弥漫 姥姥当年酿的豆瓣酱 我对著黑白照片开始想\n",
      " - 不分开不了我不能 平常话不多 除非是乌鸦抢了它的窝 它在灌木丛旁邂逅 一只令它心仪的母斑鸠 印地安老斑鸠 \n"
     ]
    }
   ],
   "source": [
    "\n",
    "num_epochs, batch_size, lr, clipping_theta = 250, 32, 1e-3, 1e-2 # 注意这里的学习率设置\n",
    "pred_period, pred_len, prefixes = 50, 50, ['分开', '不分开']\n",
    "train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,\n",
    "                            corpus_indices, idx_to_char, char_to_idx,\n",
    "                            num_epochs, num_steps, lr, clipping_theta,\n",
    "                            batch_size, pred_period, pred_len, prefixes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# GRU"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 161,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "will use cuda\n",
      "epoch 40, perplexity 153.093699, time 0.94 sec\n",
      " - 分开 我想你的让我不想想想想你你的可爱女人 坏坏的让我不想想想想你你的可爱女人 坏坏的让我不想想想想你你\n",
      " - 不分开 你不的让我不想想想想你你的可爱女人 坏坏的让我不想想想想你你的可爱女人 坏坏的让我不想想想想你你的\n",
      "epoch 80, perplexity 32.050503, time 0.95 sec\n",
      " - 分开 我想要这样 我不要再想 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我\n",
      " - 不分开 我不能再想 我不要再想 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我不 我\n",
      "epoch 120, perplexity 4.807658, time 0.96 sec\n",
      " - 分开 我想要这样坦堡 就这样的生活 我爱你 你爱我 我不能再想你 你爱你依不舍 不知不觉 我跟了这节奏 \n",
      " - 不分开 不要再这样打我妈妈 我说的话你甘会听 不要再这样打我妈妈 我说的话你甘会听 不要再这样打我妈妈 我\n",
      "epoch 160, perplexity 1.488510, time 0.95 sec\n",
      " - 分开 我想要这样牵堡在这样 想要和你融化在一起 融化在宇宙里 我每天每天每天在想想想想著你 这样的甜蜜 \n",
      " - 不分开 不要再这样打我妈妈 难道你手不会痛吗 其实我回家就想要阻止一切 让家庭回到过去甜甜 温馨的欢乐香味\n"
     ]
    }
   ],
   "source": [
    "\n",
    "\n",
    "num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size\n",
    "print('will use', device)\n",
    "\n",
    "def get_params():\n",
    "    def _one(shape):\n",
    "        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)\n",
    "        return torch.nn.Parameter(ts, requires_grad=True)\n",
    "    def _three():\n",
    "        return (_one((num_inputs, num_hiddens)),\n",
    "                _one((num_hiddens, num_hiddens)),\n",
    "                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))\n",
    "    \n",
    "    W_xz, W_hz, b_z = _three()  # 更新门参数\n",
    "    W_xr, W_hr, b_r = _three()  # 重置门参数\n",
    "    W_xh, W_hh, b_h = _three()  # 候选隐藏状态参数\n",
    "    \n",
    "    # 输出层参数\n",
    "    W_hq = _one((num_hiddens, num_outputs))\n",
    "    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)\n",
    "    return nn.ParameterList([W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q])\n",
    "\n",
    "\n",
    "def init_gru_state(batch_size, num_hiddens, device):\n",
    "    return (torch.zeros((batch_size, num_hiddens), device=device), )\n",
    "\n",
    "def gru(inputs, state, params):\n",
    "    W_xz, W_hz, b_z, W_xr, W_hr, b_r, W_xh, W_hh, b_h, W_hq, b_q = params\n",
    "    H, = state\n",
    "    outputs = []\n",
    "    for X in inputs:\n",
    "        Z = torch.sigmoid(torch.matmul(X, W_xz) + torch.matmul(H, W_hz) + b_z)\n",
    "        R = torch.sigmoid(torch.matmul(X, W_xr) + torch.matmul(H, W_hr) + b_r)\n",
    "        H_tilda = torch.tanh(torch.matmul(X, W_xh) + R * torch.matmul(H, W_hh) + b_h)\n",
    "        H = Z * H + (1 - Z) * H_tilda\n",
    "        Y = torch.matmul(H, W_hq) + b_q\n",
    "        outputs.append(Y)\n",
    "    return outputs, (H,)\n",
    "\n",
    "\n",
    "num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2\n",
    "pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']\n",
    "\n",
    "\n",
    "train_and_predict_rnn(gru, get_params, init_gru_state, num_hiddens,\n",
    "                          vocab_size, device, corpus_indices, idx_to_char,\n",
    "                          char_to_idx, False, num_epochs, num_steps, lr,\n",
    "                          clipping_theta, batch_size, pred_period, pred_len,\n",
    "                          prefixes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 163,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 40, perplexity 1.024476, time 0.11 sec\n",
      " - 分开始移动 回到当初爱你的时空 停格内容不忠 所有回忆对着我进攻       所有回忆对着我进攻    \n",
      " - 不分开不要再这样打我妈妈 我说的话 你甘会听 不要再这样打我妈妈 难道你手不会痛吗 其实我回家就想要阻止一\n",
      "epoch 80, perplexity 1.013490, time 0.10 sec\n",
      " - 分开始移动 回到当初爱你的时空 停格内容不忠 所有回忆对着我进攻 我的伤口被你拆封 誓言太沉重泪被纵容 \n",
      " - 不分开不要再这样打我妈妈 我说的话 你甘会听 不要再这样打我妈妈 难道你手不会痛吗 其实我回家就想要阻止一\n",
      "epoch 120, perplexity 1.010614, time 0.11 sec\n",
      " - 分开 它在空中停留 所有人看着我 抛物线进球 单手过人运球 篮下妙传出手 漂亮的假动作 帅呆了我 全场盯\n",
      " - 不分开 从小到大只有妈妈的温暖  为什么我爸爸 那么凶 如果真的我有一双翅膀 二双翅膀 随时出发 偷偷出发\n",
      "epoch 160, perplexity 1.010488, time 0.11 sec\n",
      " - 分开 它在空中停留 所有人看着我 抛物线进球 单手过人运球 篮下妙传出手 漂亮的假动作 帅呆了我 全场盯\n",
      " - 不分开 从小到大只有妈妈的温暖  为什么我爸爸 那么凶 如果真的我有一双翅膀 二双翅膀 随时出发 偷偷出发\n"
     ]
    }
   ],
   "source": [
    "lr = 1e-2\n",
    "gru_layer = nn.GRU(input_size=vocab_size, hidden_size=num_hiddens)\n",
    "model = RNNModel(gru_layer, vocab_size).to(device)\n",
    "train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,\n",
    "                                corpus_indices, idx_to_char, char_to_idx,\n",
    "                                num_epochs, num_steps, lr, clipping_theta,\n",
    "                                batch_size, pred_period, pred_len, prefixes)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# LSTM"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 164,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "will use cuda\n"
     ]
    }
   ],
   "source": [
    "num_inputs, num_hiddens, num_outputs = vocab_size, 256, vocab_size\n",
    "print('will use', device)\n",
    "\n",
    "def get_params():\n",
    "    def _one(shape):\n",
    "        ts = torch.tensor(np.random.normal(0, 0.01, size=shape), device=device, dtype=torch.float32)\n",
    "        return torch.nn.Parameter(ts, requires_grad=True)\n",
    "    def _three():\n",
    "        return (_one((num_inputs, num_hiddens)),\n",
    "                _one((num_hiddens, num_hiddens)),\n",
    "                torch.nn.Parameter(torch.zeros(num_hiddens, device=device, dtype=torch.float32), requires_grad=True))\n",
    "    \n",
    "    W_xi, W_hi, b_i = _three()  # 输入门参数\n",
    "    W_xf, W_hf, b_f = _three()  # 遗忘门参数\n",
    "    W_xo, W_ho, b_o = _three()  # 输出门参数\n",
    "    W_xc, W_hc, b_c = _three()  # 候选记忆细胞参数\n",
    "    \n",
    "    # 输出层参数\n",
    "    W_hq = _one((num_hiddens, num_outputs))\n",
    "    b_q = torch.nn.Parameter(torch.zeros(num_outputs, device=device, dtype=torch.float32), requires_grad=True)\n",
    "    return nn.ParameterList([W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 165,
   "metadata": {},
   "outputs": [],
   "source": [
    "def init_lstm_state(batch_size, num_hiddens, device):\n",
    "    return (torch.zeros((batch_size, num_hiddens), device=device), \n",
    "            torch.zeros((batch_size, num_hiddens), device=device))\n",
    "\n",
    "def lstm(inputs, state, params):\n",
    "    [W_xi, W_hi, b_i, W_xf, W_hf, b_f, W_xo, W_ho, b_o, W_xc, W_hc, b_c, W_hq, b_q] = params\n",
    "    (H, C) = state\n",
    "    outputs = []\n",
    "    for X in inputs:\n",
    "        I = torch.sigmoid(torch.matmul(X, W_xi) + torch.matmul(H, W_hi) + b_i)\n",
    "        F = torch.sigmoid(torch.matmul(X, W_xf) + torch.matmul(H, W_hf) + b_f)\n",
    "        O = torch.sigmoid(torch.matmul(X, W_xo) + torch.matmul(H, W_ho) + b_o)\n",
    "        C_tilda = torch.tanh(torch.matmul(X, W_xc) + torch.matmul(H, W_hc) + b_c)\n",
    "        C = F * C + I * C_tilda\n",
    "        H = O * C.tanh()\n",
    "        Y = torch.matmul(H, W_hq) + b_q\n",
    "        outputs.append(Y)\n",
    "    return outputs, (H, C)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 166,
   "metadata": {},
   "outputs": [],
   "source": [
    "num_epochs, num_steps, batch_size, lr, clipping_theta = 160, 35, 32, 1e2, 1e-2\n",
    "pred_period, pred_len, prefixes = 40, 50, ['分开', '不分开']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 167,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 40, perplexity 210.208085, time 1.19 sec\n",
      " - 分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我\n",
      " - 不分开 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我 我不的我\n",
      "epoch 80, perplexity 64.678468, time 1.13 sec\n",
      " - 分开 我想你这你 我不要这样 我不要这生 我不要这生 我不要这生 我不要这生 我不要这生 我不要这生 我\n",
      " - 不分开 我想你这你 我不要这样 我不要这生 我不要这生 我不要这生 我不要这生 我不要这生 我不要这生 我\n",
      "epoch 120, perplexity 15.337807, time 1.09 sec\n",
      " - 分开 我想我这你很着你 我想想你 你不要再看着 我想 你想很久久我 说散你的手笑有你 别么  别给我 说\n",
      " - 不分开 我想你你 你我的一话人 我想想你 我不要再难样 我想想你 你我的难手  没有你对我有多难难熬多多 \n",
      "epoch 160, perplexity 4.113094, time 1.10 sec\n",
      " - 分开 我说 你爱我的吧? 我想想你已睡很  想样的生笑笑每的看不不  我知道这里很美但家乡的你更美 就血\n",
      " - 不分开 我已你你 我不多再熬奏 我不要这生活 后知后觉 又过了一个秋 后知后觉 我该好好生活 我该好好生活\n"
     ]
    }
   ],
   "source": [
    "train_and_predict_rnn(lstm, get_params, init_lstm_state, num_hiddens,\n",
    "                          vocab_size, device, corpus_indices, idx_to_char,\n",
    "                          char_to_idx, False, num_epochs, num_steps, lr,\n",
    "                          clipping_theta, batch_size, pred_period, pred_len,\n",
    "                          prefixes)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "lr = 1e-2 # 注意调整学习率\n",
    "lstm_layer = nn.LSTM(input_size=vocab_size, hidden_size=num_hiddens)\n",
    "model = d2l.RNNModel(lstm_layer, vocab_size)\n",
    "train_and_predict_rnn_pytorch(model, num_hiddens, vocab_size, device,\n",
    "                                corpus_indices, idx_to_char, char_to_idx,\n",
    "                                num_epochs, num_steps, lr, clipping_theta,\n",
    "                                batch_size, pred_period, pred_len, prefixes)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
